package org.wu.framework.lazy.orm.core.source.h2;

import lombok.extern.slf4j.Slf4j;
import org.wu.framework.core.CamelAndUnderLineConverter;
import org.wu.framework.core.NormalUsedString;
import org.wu.framework.core.annotation.AnnotatedElementUtils;
import org.wu.framework.core.exception.AssertFactory;
import org.wu.framework.core.exception.RuntimeExceptionFactory;
import org.wu.framework.core.stereotype.LayerField;
import org.wu.framework.core.utils.ObjectUtils;
import org.wu.framework.lazy.orm.core.persistence.conf.*;
import org.wu.framework.lazy.orm.core.persistence.util.LazyTableIndexUtil;
import org.wu.framework.lazy.orm.core.persistence.util.SqlMessageFormatUtil;
import org.wu.framework.lazy.orm.core.source.advanced.SourceAdvanced;
import org.wu.framework.lazy.orm.core.source.advanced.SourceAdvancedAbstract;
import org.wu.framework.lazy.orm.core.source.clickhouse.ClickHouseColumnEndpoint;
import org.wu.framework.lazy.orm.core.source.clickhouse.ClickHouseTableLazyTableEndpoint;
import org.wu.framework.lazy.orm.core.stereotype.LazyTable;
import org.wu.framework.lazy.orm.core.stereotype.LazyTableField;
import org.wu.framework.lazy.orm.core.stereotype.LazyTableFieldId;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

@Slf4j
public abstract class H2SourceAdvancedTargetAbstract extends SourceAdvancedAbstract implements SourceAdvanced {

    private static final ConcurrentMap<Class<?>, LazyTableEndpoint<?>> H2_TABLE_CLASS_ATTR_MAP = new ConcurrentHashMap<>();

    private static final ConcurrentMap<String, LazyTableEndpoint<?>> H2_TABLE_STR_ATTR_MAP = new ConcurrentHashMap<>();
    private static final ConcurrentMap<String, LazyTableEndpoint<?>> H2_MAP_TABLE_STR_ATTR_MAP = new ConcurrentHashMap<>();


    /**
     * 特殊字段
     */
    public static List<String> specialFields = new ArrayList<String>() {
        {
            add("ALL");
            add("AND");
            add("ANY");
            add("ARRAY");
            add("AS");
            add("ASYMMETRICNR");
            add("AUTHORIZATION");
            add("BETWEENNR");
            add("BOTH");
            add("CASE");
            add("CAST");
            add("CHECK");
            add("CONSTRAINT");
            add("CROSS");
            add("CURRENT_CATALOG");
            add("CURRENT_DATE");
            add("CURRENT_PATH");
            add("CURRENT_ROLE");
            add("CURRENT_SCHEMA");
            add("CURRENT_TIME");
            add("CURRENT_TIMESTAMP");
            add("CURRENT_USER");
            add("DAY");
            add("DEFAULT");
            add("DISTINCT");
            add("ELSE");
            add("END");
            add("EXCEPT");
            add("EXISTSNR");
            add("FALSE");
            add("FETCH");
            add("FOR");
            add("FOREIGN");
            add("FROM");
            add("FULL");
            add("GROUP");
            add("GROUPS");
            add("HAVING");
            add("HOUR");
            add("IF");
            add("ILIKE");
            add("IN");
            add("INNER");
            add("INTERSECT");
            add("INTERVAL");
            add("IS");
            add("JOIN");
            add("KEYNRNRNRNR");
            add("LEADING");
            add("LEFT");
            add("LIKE");
            add("LIMITMS");
            add("LOCALTIME");
            add("LOCALTIMESTAMP");
            add("MINUSMS");
            add("MINUTE");
            add("MONTH");
            add("NATURAL");
            add("NOT");
            add("NULL");
            add("OFFSET");
            add("ON");
            add("OR");
            add("ORDER");
            add("OVER");
            add("PARTITION");
            add("PRIMARY");
            add("QUALIFY");
            add("RANGE");
            add("REGEXP");
            add("RIGHT");
            add("ROW");
            add("ROWNUM");
            add("ROWS");
            add("SECOND");
            add("SELECT");
            add("SESSION_USER");
            add("SET");
            add("SOME");
            add("SYMMETRICNR");
            add("SYSTEM_USER");
            add("TABLE");
            add("TO");
            add("TOPMS");
            add("CS");
            add("TRAILING");
            add("TRUE");
            add("UESCAPE");
            add("UNION");
            add("UNIQUE");
            add("UNKNOWN");
            add("USER");
            add("USING");
            add("VALUE");
            add("VALUES");
            add("WHEN");
            add("WHERE");
            add("WINDOW");
            add("WITH");
            add("YEAR");
            add("_ROWID_");
        }
    };

    /**
     * 根据class解析出表名称 支持自定义忽略前缀、后缀
     *
     * @param clazz 需要解析的class
     * @return 返回表名称
     * @see LazyDatabaseJsonMessage#ddlIgnoredTablePrefix
     * @see LazyDatabaseJsonMessage#ddlIgnoredTableSuffix
     */
    @Override
    public String analyzeLazyTableName(Class<?> clazz) {
        AssertFactory.notNull(clazz, "表对应的 class 不能是空");
        LazyTable
                lazyTable =
                AnnotatedElementUtils
                        .findMergedAnnotation(clazz, LazyTable.class);
        if (null == lazyTable || ObjectUtils.isEmpty(lazyTable.tableName())) {

            String simpleName = clazz.getSimpleName();
            // 适应前缀过滤
            for (String ddlIgnoredTableSuffix : LazyDatabaseJsonMessage.ddlIgnoredTablePrefix) {
                if (simpleName.startsWith(ddlIgnoredTableSuffix)) {
                    simpleName = simpleName.substring(ddlIgnoredTableSuffix.length());
                    break;
                }
            }
            // 适应后缀过滤
            for (String ddlIgnoredTableSuffix : LazyDatabaseJsonMessage.ddlIgnoredTableSuffix) {
                if (simpleName.endsWith(ddlIgnoredTableSuffix)) {
                    simpleName = simpleName.substring(0, simpleName.length() - ddlIgnoredTableSuffix.length());
                    break;
                }
            }
            return CamelAndUnderLineConverter.humpToLine2(simpleName);
        }
        return lazyTable.tableName();
    }

    /**
     * 根据字段解析出对应的表结构
     *
     * @param lazyTableInfo  表信息
     * @param lazyColumnList 字段集合
     * @return 表结构
     */
    @Override
    public LazyTableEndpoint<?> analyzeLazyTableFromLazyColumns(LazyTableInfo lazyTableInfo, List<? extends LazyColumn> lazyColumnList) {
        if(ObjectUtils.isEmpty(lazyColumnList)){
            RuntimeExceptionFactory.of("表结构字段不能为空");
        }
        String tableName = lazyTableInfo.getTableName();
        String schemaName = lazyTableInfo.getTableSchema();
        String tableComment = lazyTableInfo.getTableComment();

        if (!H2_TABLE_STR_ATTR_MAP.containsKey(tableName)) {
            synchronized (H2_TABLE_STR_ATTR_MAP) {

                List<ClickHouseColumnEndpoint> analyzeFieldOnAnnotation = lazyColumnList.stream().map(lazyColumn->{


                    String columnComment = lazyColumn.getColumnComment();
                    String columnType = lazyColumn.getColumnType();
                    String columnName = lazyColumn.getColumnName();
                    String columnKey = lazyColumn.getColumnKey();
                    String extra = lazyColumn.getExtra();
                    String isNullable = lazyColumn.getIsNullable();

                    ClickHouseColumnEndpoint lazyTableFieldEndpoint = new ClickHouseColumnEndpoint();
                    lazyTableFieldEndpoint.setName(CamelAndUnderLineConverter.lineToHumpField(columnName));
                    lazyTableFieldEndpoint.setColumnType(columnType);
                    lazyTableFieldEndpoint.setComment(columnComment);

                    lazyTableFieldEndpoint.setExist(true);
                    lazyTableFieldEndpoint.setColumnName(columnName);

                    lazyTableFieldEndpoint.setExtra(extra);
                    lazyTableFieldEndpoint.setNotNull(!NormalUsedString.YES.equalsIgnoreCase(lazyColumn.getIsNullable()));
                    lazyTableFieldEndpoint.setKey("PRI".equalsIgnoreCase(columnKey));
                    lazyTableFieldEndpoint.setDataType(lazyColumn.getDataType());
                    lazyTableFieldEndpoint.setDefaultValue(lazyColumn.getColumnDefault());
                    lazyTableFieldEndpoint.setScale(lazyColumn.getNumericScale());
                    lazyTableFieldEndpoint.setAlias(columnName);
                    return lazyTableFieldEndpoint;

                }).toList();


                boolean smartFillField = false;
                ClickHouseTableLazyTableEndpoint lazyTableEndpoint = new ClickHouseTableLazyTableEndpoint();
                if (ObjectUtils.isNotEmpty(schemaName)) {
                    lazyTableEndpoint.setSchema(schemaName);
                }
                lazyTableEndpoint.setComment(tableComment);
                lazyTableEndpoint.setTableName(tableName);
                lazyTableEndpoint.setLazyTableFieldEndpointList(analyzeFieldOnAnnotation);
                lazyTableEndpoint.setSmartFillField(smartFillField);
                lazyTableEndpoint.setExist(true);

                log.info("Initialize column list date ,tableName:[{}],comment:[{}]", tableName, tableComment);
                H2_TABLE_STR_ATTR_MAP.put(tableName, lazyTableEndpoint);
            }
        }
        return H2_TABLE_STR_ATTR_MAP.get(tableName);
    }
    /**
     * 通过class 解析出对应的表结构
     *
     * @param clazz 需要解析的class
     * @return 表结构
     */
    @Override
    public LazyTableEndpoint<?> analyzeLazyTableFromClass(Class<?> clazz) {
        if (!H2_TABLE_CLASS_ATTR_MAP.containsKey(clazz)) {
            synchronized (H2_TABLE_CLASS_ATTR_MAP) {
                LazyTable lazyTable = AnnotatedElementUtils.findMergedAnnotation(clazz, LazyTable.class);

                String className = clazz.getName();
                String tableName = analyzeLazyTableName(clazz);
                Package clazzPackage = clazz.getPackage();
                if (null == clazzPackage) {
                    System.out.println(className);
                }
                final String packageName = clazzPackage.getName();
                String comment = "";
                boolean smartFillField = false;
                boolean exist = true;

                H2TableLazyTableEndpoint lazyTableEndpoint = new H2TableLazyTableEndpoint();
                if (null != lazyTable) {
                    lazyTableEndpoint.setSchema(lazyTable.schema());
                    smartFillField = lazyTable.smartFillField();
                    comment = lazyTable.comment();
                    // 判断一手当前实体对象是否被弃用
                    if (AnnotatedElementUtils.hasAnnotation(clazz, Deprecated.class)) {
                        comment += "（即将弃用）";
                    }
                    LazyTable.Engine engine = lazyTable.engine();
                    lazyTableEndpoint.setEngine(engine);
                    exist = lazyTable.exist();
                }
                List<H2ColumnEndpoint> analyzeFieldOnAnnotation = analyzeFieldOnAnnotation(clazz, null);
                lazyTableEndpoint.setPackageName(packageName);
                lazyTableEndpoint.setComment(comment);
                lazyTableEndpoint.setClassName(className);
                lazyTableEndpoint.setClazz(clazz);
                lazyTableEndpoint.setTableName(tableName);
                lazyTableEndpoint.setLazyTableFieldEndpointList(analyzeFieldOnAnnotation);
                lazyTableEndpoint.setSmartFillField(smartFillField);
                lazyTableEndpoint.setExist(exist);
                log.info("Initialize {} annotation parameters  className:[{}],tableName:[{}],comment:[{}]", clazz,
                        className, tableName, comment);
                H2_TABLE_CLASS_ATTR_MAP.put(clazz, lazyTableEndpoint);
            }
        }
        return H2_TABLE_CLASS_ATTR_MAP.get(clazz);
    }

    /**
     * 通过bean  解析出对应的表结构
     *
     * @param schemaName 数据库
     * @param tableName  表名称
     * @param beanData   需要解析的数据
     * @return 表结构
     */
    @Override
    public LazyTableEndpoint<?> analyzeLazyTableFromMap(String schemaName, String tableName, Map<String, Object> beanData) {

        String fullTableName = ObjectUtils.isEmpty(schemaName) ? tableName : schemaName + "." + tableName;
        List<H2ColumnEndpoint> analyzeFieldOnAnnotation = beanData
                        .entrySet()
                        .stream()
                        .filter(entry -> !ObjectUtils.isEmpty(entry.getValue()))
                        .map(entry -> {
                            String key = entry.getKey();
                            Object value = entry.getValue();
                            String fieldName = key;
                            H2ColumnEndpoint lazyTableFieldEndpoint = new H2ColumnEndpoint();
                            lazyTableFieldEndpoint.setName(fieldName);
                            // 对key进行 调整
                            String tempFieldName = fieldName;
                            if (H2SourceAdvancedTargetAbstract.specialFields.contains(tempFieldName.toUpperCase())) {
                                fieldName = "`" + fieldName + "`";
                            }
                            Class<?> fieldClazz = null == value ? String.class : value.getClass();
                            lazyTableFieldEndpoint.setColumnType(LazyTableField.FieldType.getH2TypeByClass(fieldClazz));
                            lazyTableFieldEndpoint.setComment(String.format("数据格式如:%s", value));
                            lazyTableFieldEndpoint.setClazz(fieldClazz);
                            lazyTableFieldEndpoint.setExist(true);
                            lazyTableFieldEndpoint.setColumnName(fieldName);
                            lazyTableFieldEndpoint.setFieldValue(value);
                            return lazyTableFieldEndpoint;
                        }).toList();

                String comment = "通过Map数据创建的表";
                boolean smartFillField = false;
                H2TableLazyTableEndpoint lazyTableEndpoint = new H2TableLazyTableEndpoint();

                if (ObjectUtils.isNotEmpty(schemaName)) {
                    lazyTableEndpoint.setSchema(schemaName);
                }
                lazyTableEndpoint.setComment(comment);
                lazyTableEndpoint.setTableName(tableName);
                lazyTableEndpoint.setLazyTableFieldEndpointList(analyzeFieldOnAnnotation);
                lazyTableEndpoint.setSmartFillField(smartFillField);
                lazyTableEndpoint.setExist(true);
        log.info("Initialize map date ,tableName:[{}],comment:[{}]", fullTableName, comment);
        H2_MAP_TABLE_STR_ATTR_MAP.put(fullTableName, lazyTableEndpoint);

        return H2_MAP_TABLE_STR_ATTR_MAP.get(fullTableName);
    }

    /**
     * 获取 表字段信息实例
     *
     * @return 表字段信息实例
     */
    @Override
    public LazyTableFieldEndpoint getLazyTableFieldEndpointInstance() {
        return new H2ColumnEndpoint();
    }

    /**
     * 获取数据库表实例
     *
     * @return 数据库表实例
     */
    @Override
    public LazyTableEndpoint<?> getLazyTableEndpointInstance() {
        return new H2TableLazyTableEndpoint();
    }

    /**
     * 当前数据类型缓存的表结构
     *
     * @return 当前数据类型缓存的表结构
     */
    @Override
    public ConcurrentMap<Class<?>, LazyTableEndpoint<?>> getClassTableCache() {
        return H2_TABLE_CLASS_ATTR_MAP;
    }

    /**
     * 当前数据类型缓存的表结构
     *
     * @return 当前数据类型缓存的表结构
     */
    @Override
    public ConcurrentMap<String, LazyTableEndpoint<?>> getMapTableCache() {
        return H2_MAP_TABLE_STR_ATTR_MAP;
    }

    /**
     * 解析 class 表结构中的字段
     *
     * @param clazz              需要解析的class
     * @param tableFileIndexType 过滤字段索引类型
     * @return 返回表结构中字段
     */
    @Override
    public <T> List<H2ColumnEndpoint> analyzeFieldOnAnnotation(Class<T> clazz, LayerField.LayerFieldType tableFileIndexType) {
        // 有主键
        boolean haveId = false;
        List<H2ColumnEndpoint> convertedFieldList = new ArrayList<>();
        for (Field declaredField : clazz.getDeclaredFields()) {
//            if (!Modifier.isFinal(declaredField.getModifiers()) &&!declaredField.isAccessible()) {
//                declaredField.setAccessible(true);
//            }
            declaredField.setAccessible(true);
            LazyTableField lazyTableField = AnnotatedElementUtils.findMergedAnnotation(declaredField, LazyTableField.class);
            String columnName = CamelAndUnderLineConverter.humpToLine2(declaredField.getName());
            H2ColumnEndpoint lazyTableFieldEndpoint = new H2ColumnEndpoint();
            String alias = columnName;

            boolean notNull = false;
            boolean key = false;
            String defaultValue = null;
            if (!ObjectUtils.isEmpty(lazyTableField)) {
                lazyTableFieldEndpoint.setExist(lazyTableField.exist());
                if (!lazyTableField.exist()) {
                    continue;
                }
                String comment = lazyTableField.comment();
                // 判断一手当前实体对象字段是否被弃用
                if (AnnotatedElementUtils.hasAnnotation(declaredField, Deprecated.class)) {
                    comment += "（即将弃用）";
                }
                LazyTableIndexEndpoint[] lazyTableIndexEndpoints = LazyTableIndexUtil.analyzeFieldIndex(lazyTableField);
                // 判断是否是我想要的类型
                if (!ObjectUtils.isEmpty(tableFileIndexType)
                        && !ObjectUtils.isEmpty(lazyTableIndexEndpoints)
                        && Arrays.stream(lazyTableIndexEndpoints).noneMatch(lazyTableIndexEndpoint -> tableFileIndexType.equals(lazyTableIndexEndpoint.getFieldIndexType()))
                ) {
                    continue;
                }
                if (!ObjectUtils.isEmpty(lazyTableField.value())) {
                    columnName = lazyTableField.value();
                }
                notNull = lazyTableField.notNull();
                key = lazyTableField.key();
                defaultValue = ObjectUtils.isEmpty(lazyTableField.defaultValue()) ? null : lazyTableField.defaultValue();
                lazyTableFieldEndpoint.setComment(comment);
                lazyTableFieldEndpoint.setColumnType(lazyTableField.columnType());
                lazyTableFieldEndpoint.setUpsertStrategy(lazyTableField.upsertStrategy());
                lazyTableFieldEndpoint.setUpdateStrategy(lazyTableField.updateStrategy());
                lazyTableFieldEndpoint.setExtra(lazyTableField.extra());
                lazyTableFieldEndpoint.setLazyTableIndexEndpoints(lazyTableIndexEndpoints);
                lazyTableFieldEndpoint.setSerialNumber(lazyTableField.serialNumber());
            } else {
                lazyTableFieldEndpoint.setExist(true);
            }
            // 判断并获取主键ID
            if (!haveId) {
                final LazyTableFieldId lazyTableFieldId = AnnotatedElementUtils.findMergedAnnotation(declaredField, LazyTableFieldId.class);
                if (!ObjectUtils.isEmpty(lazyTableFieldId)) {
                    haveId = true;
                    LazyTableFieldId.IdType idType = lazyTableFieldId.idType();
                    lazyTableFieldEndpoint.setIdType(idType);
                    if (LazyTableFieldId.IdType.INPUT_ID.equals(idType)) {
                        lazyTableFieldEndpoint.setExtra("");
                    }
                }
            }
            lazyTableFieldEndpoint.setField(declaredField);
            lazyTableFieldEndpoint.setColumnName(columnName);
            lazyTableFieldEndpoint.setName(declaredField.getName());
            lazyTableFieldEndpoint.setClazz(declaredField.getType());
            lazyTableFieldEndpoint.setAlias(alias);
            lazyTableFieldEndpoint.setNotNull(notNull);
            lazyTableFieldEndpoint.setKey(key);
            lazyTableFieldEndpoint.setDefaultValue(defaultValue);
            convertedFieldList.add(lazyTableFieldEndpoint);
        }
        convertedFieldList =
                convertedFieldList.stream().sorted(Comparator.comparing(LazyTableFieldEndpoint::getSerialNumber)).collect(Collectors.toList());

        return convertedFieldList;
    }

    /**
     * 关键字转译
     *
     * @param column 数据库字段 <p>user_id</p>
     * @return 转译后数据
     */
    @Override
    public String cleanSpecialColumn(String column) {
        AssertFactory.notNull(column, "列字段不能为空");
        if (specialFields.contains(column.toUpperCase(Locale.ROOT))) {
            return NormalUsedString.BACKTICK + column + NormalUsedString.BACKTICK;
        }
        return column;
    }

    /**
     * 获取数据库schema
     *
     * @return 数据库schema
     */
    @Override
    public String findSchemaListSql() {
        return "show databases;";
    }

    /**
     * 查询原始表字段sql
     *
     * @param schema 数据库
     * @param table  表
     * @return 原始表字段sql
     */
    @Override
    public String findTableColumnListSql(String schema, String table) {
        String sqlFormat = "select * from information_schema.COLUMNS where (table_catalog =  '{0}' or table_catalog =  '{1}') and ( table_name='{2}' or  table_name='{3}' ) ";
        return SqlMessageFormatUtil.format(sqlFormat, schema.toLowerCase(),schema.toUpperCase(), table.toLowerCase(), table.toUpperCase());
    }

    /**
     * 查询索引sql
     *
     * @param schema 数据库
     * @param table  表
     * @return 查询索引sql
     */
    @Override
    public String findTableColumnIndexListSql(String schema, String table) {
        String sqlFormat = "SELECT * FROM INFORMATION_SCHEMA.INDEX_COLUMNS WHERE (table_catalog =  '{0}' or table_catalog =  '{1}') and ( table_name='{2}' or  table_name='{3}' )";
        return SqlMessageFormatUtil.format(sqlFormat, schema.toLowerCase(),schema.toUpperCase(), table.toLowerCase(), table.toUpperCase());
    }

    /**
     * 查询表信息 sql
     *
     * @param schema 数据库
     * @param table  表
     * @return 查询表信息sql
     */
    @Override
    public String findTableInfoSql(String schema, String table) {
        String sqlFormat = "select * from information_schema.tables where (table_catalog =  '{0}' or table_catalog =  '{1}') and ( table_name='{2}' or  table_name='{3}' )";
        return SqlMessageFormatUtil.format(sqlFormat, schema.toLowerCase(),schema.toUpperCase(), table.toLowerCase(), table.toUpperCase());
    }

    /**
     * 创建 database语句
     *
     * @param schemaName 数据库名称
     * @return 创建database语句
     */
    @Override
    public String createSchemaSql(String schemaName) {
        String sqlFormat = "CREATE DATABASE IF NOT EXISTS `{0}`";
        return SqlMessageFormatUtil.format(sqlFormat, schemaName);
    }
}
